#!/usr/bin/env python3
# -*- coding: utf-8 -*-
"""
OSS管理器模块
提供阿里云OSS文件上传和管理功能

作者: Wang Zhen
版本: 1.0.0
"""

import os
import json
import logging
from typing import Dict, Any, Optional, List
from pathlib import Path

try:
    import oss2
    OSS_AVAILABLE = True
except ImportError:
    OSS_AVAILABLE = False
    logging.warning("oss2模块未安装，OSS功能将不可用")

logger = logging.getLogger(__name__)

class OSSManager:
    """阿里云OSS管理器"""

    def __init__(self, access_key_id: str = None, access_key_secret: str = None,
                 bucket_name: str = "limereader-releases", endpoint: str = "oss-cn-hangzhou.aliyuncs.com"):
        """
        初始化OSS管理器

        Args:
            access_key_id: 阿里云AccessKey ID
            access_key_secret: 阿里云AccessKey Secret
            bucket_name: OSS Bucket名称
            endpoint: OSS Endpoint
        """
        self.access_key_id = access_key_id or os.getenv('ALI_OSS_ACCESS_KEY_ID')
        self.access_key_secret = access_key_secret or os.getenv('ALI_OSS_ACCESS_KEY_SECRET')
        self.bucket_name = bucket_name
        self.endpoint = endpoint

        self.auth = None
        self.bucket = None

        if OSS_AVAILABLE and self.access_key_id and self.access_key_secret:
            self._init_oss()
        else:
            logger.warning("OSS未正确初始化")

    def _init_oss(self):
        """初始化OSS连接"""
        try:
            self.auth = oss2.Auth(self.access_key_id, self.access_key_secret)
            self.bucket = oss2.Bucket(self.auth, self.endpoint, self.bucket_name)
            logger.info(f"OSS连接初始化成功: {self.bucket_name}")
        except Exception as e:
            logger.error(f"OSS连接初始化失败: {e}")
            self.auth = None
            self.bucket = None

    def is_available(self) -> bool:
        """检查OSS是否可用"""
        return OSS_AVAILABLE and self.bucket is not None

    def upload_file(self, local_path: str, oss_path: str) -> bool:
        """
        上传文件到OSS

        Args:
            local_path: 本地文件路径
            oss_path: OSS文件路径

        Returns:
            bool: 上传是否成功
        """
        if not self.is_available():
            logger.error("OSS不可用，无法上传文件")
            return False

        try:
            local_file = Path(local_path)
            if not local_file.exists():
                logger.error(f"本地文件不存在: {local_path}")
                return False

            logger.info(f"开始上传文件: {local_path} -> {oss_path}")
            self.bucket.put_object_from_file(oss_path, str(local_file))
            logger.info(f"文件上传成功: {oss_path}")
            return True

        except Exception as e:
            logger.error(f"文件上传失败: {e}")
            return False

    def upload_json(self, data: Dict[str, Any], oss_path: str) -> bool:
        """
        上传JSON数据到OSS

        Args:
            data: JSON数据
            oss_path: OSS文件路径

        Returns:
            bool: 上传是否成功
        """
        if not self.is_available():
            logger.error("OSS不可用，无法上传JSON")
            return False

        try:
            json_str = json.dumps(data, indent=2, ensure_ascii=False)
            logger.info(f"开始上传JSON数据: {oss_path}")
            self.bucket.put_object(oss_path, json_str)
            logger.info(f"JSON数据上传成功: {oss_path}")
            return True

        except Exception as e:
            logger.error(f"JSON数据上传失败: {e}")
            return False

    def file_exists(self, oss_path: str) -> bool:
        """
        检查OSS文件是否存在

        Args:
            oss_path: OSS文件路径

        Returns:
            bool: 文件是否存在
        """
        if not self.is_available():
            return False

        try:
            return self.bucket.object_exists(oss_path)
        except Exception as e:
            logger.error(f"检查文件存在性失败: {e}")
            return False

    def get_file_info(self, oss_path: str) -> Optional[Dict[str, Any]]:
        """
        获取OSS文件信息

        Args:
            oss_path: OSS文件路径

        Returns:
            Dict: 文件信息，失败时返回None
        """
        if not self.is_available():
            return None

        try:
            meta = self.bucket.get_object_meta(oss_path)
            return {
                'size': meta.content_length,
                'last_modified': meta.last_modified,
                'content_type': meta.content_type,
                'etag': meta.etag
            }
        except Exception as e:
            logger.error(f"获取文件信息失败: {e}")
            return None

    def delete_file(self, oss_path: str) -> bool:
        """
        删除OSS文件

        Args:
            oss_path: OSS文件路径

        Returns:
            bool: 删除是否成功
        """
        if not self.is_available():
            logger.error("OSS不可用，无法删除文件")
            return False

        try:
            self.bucket.delete_object(oss_path)
            logger.info(f"文件删除成功: {oss_path}")
            return True
        except Exception as e:
            logger.error(f"文件删除失败: {e}")
            return False

    def list_files(self, prefix: str = "", max_keys: int = 100) -> List[str]:
        """
        列出OSS文件

        Args:
            prefix: 文件前缀
            max_keys: 最大返回数量

        Returns:
            List[str]: 文件列表
        """
        if not self.is_available():
            return []

        try:
            files = []
            for obj in oss2.ObjectIterator(self.bucket, prefix=prefix, max_keys=max_keys):
                files.append(obj.key)
            return files
        except Exception as e:
            logger.error(f"列出文件失败: {e}")
            return []

    def get_file_url(self, oss_path: str, expires: int = 3600) -> str:
        """
        获取OSS文件的临时访问URL

        Args:
            oss_path: OSS文件路径
            expires: 过期时间（秒）

        Returns:
            str: 临时URL
        """
        if not self.is_available():
            return ""

        try:
            url = self.bucket.sign_url('GET', oss_path, expires)
            return url
        except Exception as e:
            logger.error(f"生成URL失败: {e}")
            return ""

    def sync_directory(self, local_dir: str, oss_prefix: str,
                      delete_missing: bool = False) -> bool:
        """
        同步目录到OSS

        Args:
            local_dir: 本地目录
            oss_prefix: OSS前缀
            delete_missing: 是否删除OSS中多余的文件

        Returns:
            bool: 同步是否成功
        """
        if not self.is_available():
            logger.error("OSS不可用，无法同步目录")
            return False

        try:
            local_path = Path(local_dir)
            if not local_path.exists():
                logger.error(f"本地目录不存在: {local_dir}")
                return False

            success_count = 0
            error_count = 0

            # 上传本地文件
            for local_file in local_path.rglob('*'):
                if local_file.is_file():
                    relative_path = local_file.relative_to(local_path)
                    oss_path = f"{oss_prefix}/{relative_path}".replace('\\', '/')

                    if self.upload_file(str(local_file), oss_path):
                        success_count += 1
                    else:
                        error_count += 1

            # 删除OSS中多余的文件
            if delete_missing:
                oss_files = set(self.list_files(oss_prefix))
                local_files = set()

                for local_file in local_path.rglob('*'):
                    if local_file.is_file():
                        relative_path = local_file.relative_to(local_path)
                        oss_path = f"{oss_prefix}/{relative_path}".replace('\\', '/')
                        local_files.add(oss_path)

                files_to_delete = oss_files - local_files
                for file_path in files_to_delete:
                    if self.delete_file(file_path):
                        logger.info(f"删除多余文件: {file_path}")

            logger.info(f"目录同步完成: 成功 {success_count}, 失败 {error_count}")
            return error_count == 0

        except Exception as e:
            logger.error(f"目录同步失败: {e}")
            return False


# 便捷函数
def create_oss_manager(config_file: str = "oss_config.json") -> OSSManager:
    """
    从配置文件创建OSS管理器

    Args:
        config_file: 配置文件路径

    Returns:
        OSSManager: OSS管理器实例
    """
    config_path = Path(config_file)

    if config_path.exists():
        try:
            with open(config_path, 'r', encoding='utf-8') as f:
                config = json.load(f)

            return OSSManager(
                access_key_id=config.get('access_key_id'),
                access_key_secret=config.get('access_key_secret'),
                bucket_name=config.get('bucket_name', 'limereader-releases'),
                endpoint=config.get('endpoint', 'oss-cn-hangzhou.aliyuncs.com')
            )
        except Exception as e:
            logger.error(f"读取配置文件失败: {e}")

    # 使用默认配置
    return OSSManager()


if __name__ == "__main__":
    # 测试代码
    logging.basicConfig(level=logging.INFO)

    oss_manager = create_oss_manager()

    if oss_manager.is_available():
        print("OSS管理器初始化成功")

        # 测试上传
        test_data = {"test": "hello", "time": "2025-01-01"}
        if oss_manager.upload_json(test_data, "test/upload.json"):
            print("测试上传成功")
        else:
            print("测试上传失败")
    else:
        print("OSS管理器不可用，请检查配置")